﻿// <copyright file="NodeHelpers.cs" company="Auros Ltd.">
//     Copyright (c) Auros Ltd. All rights reserved.
// </copyright>

namespace Auros.Categorisation
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using umbraco.NodeFactory;

    /// <summary>
    /// Class of helper methods for umbraco.NodeFactory.Node.
    /// </summary>
    public static class NodeHelpers
    {
        /// <summary>
        /// Gets all nodes which have been assigned the specified category.
        /// </summary>
        /// <param name="propertyAlias">The alias of the property which is used for storing the associated category IDs.</param>
        /// <param name="category">The category.</param>
        /// <param name="rootId">The root node ID.</param>
        /// <returns>Collection of nodes.</returns>
        public static Node[] GetNodesInCategory(string propertyAlias, Category category, int rootId = -1)
        {
            return NodeHelpers.GetNodesInCategory(propertyAlias, category.ID, rootId);
        }

        /// <summary>
        /// Gets the nodes in category.
        /// </summary>
        /// <param name="propertyAlias">The alias of the property which is used for storing the associated category IDs.</param>
        /// <param name="categoryId">The category ID.</param>
        /// <param name="rootId">The root node ID.</param>
        /// <returns>Collection of nodes.</returns>
        public static Node[] GetNodesInCategory(string propertyAlias, int categoryId, int rootId = -1)
        {
            Node rootNode = new Node(-1);

            return rootNode.GetDescendantNodes(n => n.HasCategoryAssigned(propertyAlias, categoryId)).ToArray();
        }

        /// <summary>
        /// Nodes the has category assigned.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="propertyAlias">The alias of the property which is used for storing the associated category IDs.</param>
        /// <param name="categoryId">The category id.</param>
        /// <returns>True if the node has the specified category assigned to it, otherwise false.</returns>
        public static bool NodeHasCategoryAssigned(Node node, string propertyAlias, int categoryId)
        {
            if (node == null)
                throw new ArgumentNullException("node");

            if (propertyAlias == null)
                throw new ArgumentNullException("propertyAlias");

            if (string.IsNullOrEmpty(propertyAlias))
                throw new ArgumentException("Input cannot be empty.", "propertyAlias");

            var property = node.GetProperty(propertyAlias);

            if (property != null)
                return property.GetValueAsIntArray().Contains(categoryId);
            else
                return false;
        }

        /// <summary>
        /// Gets the descendant nodes from the current node.
        /// </summary>
        /// <param name="node">The root node.</param>
        /// <param name="func">The function to filter nodes.</param>
        /// <param name="includeDescendantsOfNonMatches">If set to <c>true</c> then descendants of non-matching nodes will be included in the query.</param>
        /// <returns>Collection of nodes.</returns>
        public static IEnumerable<umbraco.NodeFactory.Node> GetDescendantNodes(umbraco.NodeFactory.Node node, Func<umbraco.NodeFactory.Node, bool> func = null, bool includeDescendantsOfNonMatches = true)
        {
            foreach (umbraco.NodeFactory.Node child in node.Children)
            {
                bool isMatch = func != null ? func(child) : true;

                if (isMatch)
                    yield return child;

                if (isMatch || includeDescendantsOfNonMatches)
                {
                    foreach (umbraco.NodeFactory.Node grandchild in child.GetDescendantNodes(func, includeDescendantsOfNonMatches))
                    {
                        yield return grandchild;
                    }
                }
            }

            yield break;
        }

        /// <summary>
        /// Gets the categories for the specified node.
        /// </summary>
        /// <param name="currentNode">The node to get the categories from.</param>
        /// <param name="propertyAlias">The alias of the property which is used for storing the associated category IDs.</param>
        /// <returns>Collection of category objects.</returns>
        public static IEnumerable<Category> GetCategoriesForNode(umbraco.NodeFactory.Node currentNode, string propertyAlias)
        {
            if (propertyAlias == null)
                throw new ArgumentNullException("propertyAlias");

            if (string.IsNullOrEmpty(propertyAlias))
                throw new ArgumentException("Input cannot be empty.", "propertyAlias");

            var property = currentNode.GetProperty(propertyAlias);

            if (property != null && !string.IsNullOrEmpty(property.Value))
                return Categories.GetCategories(property.GetValueAsIntArray());
            else
                return new Category[0];
        }
    }
}